import { NGXLogger } from 'ngx-logger';
import { Participant } from './../entity/TT';
import { Utils } from './../common/Common';
import { Injectable } from "@angular/core";
import { environment } from "src/environments/environment";
import {
  EventService,
  EventMessage,
  EventSource,
  ParticipantMoveEvent,
  ParticipantSelectEvent,
  ParticipantSizeEvent,
  ParticipantGoHomeEvent,
  ParticipantMuteChangeEvent,
} from "../services/event.service";
import { RequestReplyService } from "./request-reply.service";
import { User, AuthUser, OPosition } from "../entity/TT";
import {
  TrackType,
  Track,
  TrackWrapper,
  CONNECTION_STATUS,
} from "../entity/ORTC";
import { PainterEvent, ToolType } from "../entity/ToolItem";

const TAG = "OrtcService";

export class XMPPMessage {
  id: string;
  text: string;
  ts: number;
}

@Injectable({
  providedIn: "root",
})
export class OrtcService {
  options: any;

  // JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
  initOptions: any = {
    disableAudioLevels: true,

    // The ID of the jidesha extension for Chrome.
    desktopSharingChromeExtId: "mbocklcggfhnbahlnepmldehdhpjfcjp",

    // Whether desktop sharing should be disabled on Chrome.
    desktopSharingChromeDisabled: true,

    // The media sources to use when using screen sharing with the Chrome
    // extension.
    desktopSharingChromeSources: ["screen", "window"],

    // Required version of Chrome extension
    desktopSharingChromeMinExtVersion: "0.1",

    // Whether desktop sharing should be disabled on Firefox.
    desktopSharingFirefoxDisabled: true,
  };

  confOptions = {
    openBridgeChannel: true,
  };

  participants = [];
  localTracks : any[] = [];
  remoteTracks: TrackWrapper[] = [];
  isJoined = false;

  connection: any = null;
  room: any = null;
  smartBoard: any = null;

  constructor(
    private log: NGXLogger,
    private eventService: EventService,
    private requestService: RequestReplyService
  ) {
    this.eventService.subscribeExit(() => {
      this.dispose();
    });
  }

  init(user: AuthUser) {
    this.log.debug("init user:", user);

    this.requestService.GetRtc().subscribe((options) => {
      options.bosh = environment.bindUrl;

      // options.hosts.anonymousdomain='meet.jitsi'
      this.options = options;
      this.log.debug("options", this.options);
      this.initJitsi();

      //
      // id: user.uin + '@' + this.options.hosts.domain,
      // password: user.password
      this.initConnection({
        id: user.uin + "@" + options.hosts.domain,
        password: user.password,
      });
      //anonymousdomain,
    });
  }

  initJitsi() {
    this.log.debug("initOptions", this.initOptions);

    JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.INFO);
    JitsiMeetJS.init(this.initOptions);
  }

  initConnection(connOptions: any) {
    this.log.debug("initConnection connOptions:", connOptions);

    this.connection = new JitsiMeetJS.JitsiConnection(null, null, this.options);

    this.connection.addEventListener(
      JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED,
      this.onConnectionSuccess.bind(this)
    );

    this.connection.addEventListener(
      JitsiMeetJS.events.connection.CONNECTION_FAILED,
      this.onConnectionFailed.bind(this)
    );

    this.connection.addEventListener(
      JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED,
      this.onDisconnect.bind(this)
    );

    this.log.debug("connect using:", connOptions);
    // this.connection.connect(connOptions);
    this.connection.connect(connOptions); //anonymous
  }

  sendText(text: string): boolean {
    if (!text) {
      return false;
    }

    if (!this.room) {
      this.log.debug("room is null");
      return;
    }

    this.room.sendTextMessage(text);
    return true;
  }

  joinConference(conference: string) {
    if (!conference) {
      console.warn("conference is invalid");
      return;
    }

    conference = conference.toLocaleLowerCase();
    this.log.debug("joinConference:", conference);

    if (!this.connection) {
      console.warn("connection is invalid");
      return;
    }

    JitsiMeetJS.mediaDevices.addEventListener(
      JitsiMeetJS.events.mediaDevices.DEVICE_LIST_CHANGED,
      this.onDeviceListChanged.bind(this)
    );

    const createPromise = JitsiMeetJS.createLocalTracks({
      devices: ["audio", "video"],
    })
      .then(this.onLocalTracks.bind(this))
      .catch((error) => {
        this.eventService.sendError(error.message);
        this.log.error("createLocalTracks", error);
        throw error;
      });
    this.log.error('create local tracks=>', createPromise);

    if (JitsiMeetJS.mediaDevices.isDeviceChangeAvailable("output")) {
      this.log.debug(
        "mediaDevices.enumerateDevices",
        JitsiMeetJS.mediaDevices.enumerateDevices
      );
      JitsiMeetJS.mediaDevices.enumerateDevices((devices) => {
        devices.forEach(device => {
          this.log.debug(TAG, `device: ${device}`);
        });
        // const audioOutputDevices = devices.filter(
        //   (d) => d.kind === "audiooutput"
        // );

        // if (audioOutputDevices.length > 1) {
        //   $("#audioOutputSelect").html(
        //     audioOutputDevices
        //       .map((d) => `<option value="${d.deviceId}">${d.label}</option>`)
        //       .join("\n")
        //   );

        //   $("#audioOutputSelectWrapper").show();
        // }
      });
    }

    try {
      this.room = this.connection.initJitsiConference(
        conference,
        this.confOptions
      );
    } catch (e) {
      this.log.debug("initJitsiConference", e);
      return;
    }

    this.room.on(
      JitsiMeetJS.events.conference.TRACK_ADDED,
      this.onTrackAdded.bind(this)
    );

    this.room.on(JitsiMeetJS.events.conference.TRACK_REMOVED,
      //  (track) => {this.log.debug(TAG, `track removed!!!${track}`);}
      this.onTrackRemoved.bind(this)
       );

    this.room.on(
      JitsiMeetJS.events.conference.CONFERENCE_JOINED,
      this.onConferenceJoined.bind(this)
    );

    this.room.on(
      JitsiMeetJS.events.conference.USER_JOINED,
      this.onUserJoined.bind(this)
    );

    this.room.on(
      JitsiMeetJS.events.conference.USER_LEFT,
      this.onUserLeft.bind(this)
    );

    this.room.on(JitsiMeetJS.events.conference.LAST_N_ENDPOINTS_CHANGED,
       (leavingEndpointIds, enteringEndpointIds)=>{
      this.log.debug(TAG, 'LAST_N_ENDPOINTS_CHANGED',
      'leaving', leavingEndpointIds, 'entering', enteringEndpointIds);
      leavingEndpointIds.forEach(e =>{this.removeParticipant(e)});
    });

    this.room.on(JitsiMeetJS.events.conference.ENDPOINT_CONN_STATUS_CHANGED,
      (endpoint, isActive)=>{
     this.log.debug(TAG, 'ENDPOINT_CONN_STATUS_CHANGED', endpoint, isActive);
   });

    this.room.on(JitsiMeetJS.events.conference.TRACK_MUTE_CHANGED, (track) => {
      const pId = track.getParticipantId();
      this.log.debug(`${pId} - ${track.getType()} isMuted - ${track.isMuted()}`);

      const pmce: ParticipantMuteChangeEvent = {
        participantId: pId,
        active: track.isMuted(),
      };
      this.eventService.sendMuteChanged(pmce);
    });

    this.room.on(
      JitsiMeetJS.events.conference.MESSAGE_RECEIVED,
      (id: string, text: string, ts: number = new Date().getTime()) => {
        this.log.debug(TAG, "MESSAGE_RECEIVED:", id, text, ts);
        const xmppMessage: XMPPMessage = { id, text, ts };
        this.eventService.sendXMPPMessage(xmppMessage);
        //<message to='70110720-e2ee-40f9-943c-a523dffb240f@meet.jit.si/uuU4saVd' from='test@conference.meet.jit.si/70110720' type='groupchat' xmlns='jabber:client'><body>rrr</body><nick xmlns='http://jabber.org/protocol/nick'>44</nick></message>
        return true;
      }
    );

    this.room.on(
      JitsiMeetJS.events.conference.DISPLAY_NAME_CHANGED,
      (userID, displayName) => this.log.debug(`${userID} - ${displayName}`)
    );

    this.room.on(
      JitsiMeetJS.events.conference.TRACK_AUDIO_LEVEL_CHANGED,
      (userID, audioLevel) => this.log.debug(`${userID} - ${audioLevel}`)
    );

    this.room.on(JitsiMeetJS.events.conference.PHONE_NUMBER_CHANGED, () =>
      this.log.debug(`${this.room.getPhoneNumber()} - ${this.room.getPhonePin()}`)
    );

    this.room.join();
    this.log.debug(TAG, "my participantId:", this.myParticipantId());

    // const me = this.requestService.LoadMe();
    // if (me) {
    //     me.participant = this.myParticipantId();
    // }
  }

  getXmpp() {
    return this.connection.xmpp;
  }

  initSmartBoard() {
    const xmpp = this.getXmpp();
    if (!xmpp) {
      console.warn("xmpp", xmpp);
      return false;
    }
    if (!xmpp.loadSmartboard) {
      console.warn("smartboard was not installed");
      return false;
    }
      const domain = this.options.hosts.domain
      this.log.debug('loadSmartboard', domain)
      this.smartBoard = xmpp.loadSmartboard(
        domain ,
        this.onController.bind(this),
        this.onDraw.bind(this)
      );

      this.eventService.subscribeDraw((draw: PainterEvent) => {
        // this.log.debug("subscribeDraw", draw);
        this.smartBoard.sendDraw(this.myParticipantId(), [0], draw);
      });

      this.eventService.subscribeEvent((evt: EventMessage) => {
        switch (evt.source) {
          case EventSource.TOGGLE_TOOLBOX:
            this.sendControllerTogglePainter(evt.data);
            break;

          case EventSource.PARTICIPANT_MOVE:
            this.sendControllerStudentMove(evt.data);
            break;

          case EventSource.PARTICIPANT_SELECT:
            this.sendControllerStudentSelect(evt.data);
            break;

          case EventSource.PARTICIPANT_SIZE:
            const data: ParticipantSizeEvent = evt.data;
            this.smartBoard.sendControl(
              this.myParticipantId(),
              [data.participantId],
              { "participant-size": data.size }
            );
            break;
          case EventSource.MUTE_VIDEO:
            this.toggleMute(TrackType.video, !evt.data);
            break;
        }
      });

  }

  onDraw($draw, ts) {
    this.log.debug("onDraw", ts);

    const participants = $draw.find("participants").text().split(",");
    this.log.debug("participants", participants);
    const from = $draw.find("from").text();

    if (this.myParticipantId() == from) {
      this.log.debug("is mine");
      return;
    }

    const $data = $draw.find("draw>data");

    const drawType: ToolType = $data.find("type").text();
    const $drawData = $data.find("data");
    let data;
    switch (drawType) {
      case ToolType.TEXT:
        data = {
          text: $drawData.find("text").text(),
          font: $drawData.find("font").text(),
          color: $drawData.find("color").text(),
          position: {
            x: $drawData.find("position>x").text(),
            y: $drawData.find("position>y").text(),
          },
        };
        break;
      // case ToolType.PEN:

      // data = {  width: $drawData.find('width').text(),
      //           color: $drawData.find('color').text(),
      //           points: $drawData.find('points').text()
      //         }

      // break;
      case ToolType.MOVE_SECTION:
        data = {
          x: $drawData.find("x").text(),
          y: $drawData.find("y").text(),
          width: $drawData.find("width").text(),
          height: $drawData.find("height").text(),
        };
        break;
      case ToolType.MOVE_MOVE:
        data = { x: $drawData.find("x").text(), y: $drawData.find("y").text() };
        break;
      default:
        data = $drawData.text();
        break;
    }

    const pe: PainterEvent = { type: drawType, data };
    // this.log.debug("PainterEvent", pe);

    this.eventService.sendPaintChange(pe);
  }

  onController($controller, ts) {
    // this.log.debug("onController", ts);

    const myId = this.myParticipantId();
    this.log.debug("myId", myId);

    const from = $controller.find("from").text();
    this.log.debug("from", from);

    const participants = $controller.find("participants").text().split(",");
    this.log.debug("participants", participants);

    if (!participants || participants.length < 1) {
      return;
    }

    const isMine = participants.find((p) => p == myId);
    if (isMine) {
      this.log.debug("has me");
      const voice = $controller.find("data>voice").text();
      if (voice != null && voice != "") {
        const active: boolean = voice == "true";
        this.toggleMute(TrackType.audio, active);
      }

      const whiteboard = $controller.find("data>whiteboard").text();
      if (whiteboard != null && whiteboard != "") {
        this.eventService.sendWhiteBoardChanged(whiteboard == "true");
      }
    }

    if (from != myId) {
      // not mine
      const $participantMove = $controller.find("data>participant-move");
      if ($participantMove.length > 0) {
        const x = $participantMove.find("x").text();
        const y = $participantMove.find("y").text();

        const p: OPosition = { x: Number.parseInt(x), y: Number.parseInt(y) };

        const data: ParticipantMoveEvent = {
          participantId: participants[0],
          position: p,
        };

        this.eventService.sendEvent(EventSource.PARTICIPANT_MOVED, data);
      }

      const select = $controller.find("data>participant-select").text();
      if (select != null && select != "") {
        const data: ParticipantSelectEvent = {
          participantIds: participants,
          select: select == "true",
        };

        this.eventService.sendEvent(EventSource.PARTICIPANT_SELECTED, data);
      }

      const width = $controller.find("data>participant-size>width").text();
      const height = $controller.find("data>participant-size>height").text();
      if (width && height) {
        const data: ParticipantSizeEvent = {
          participantId: participants[0],
          size: { width, height },
        };

        this.eventService.sendEvent(EventSource.PARTICIPANT_SIZE_CHANGED, data);
      }
    }

    const gift = $controller.find("data>gift").text();
    if (gift != null && gift != "") {
      this.eventService.sendGiftChanged(participants);
    }

    const togglePainter = $controller.find("data>togglePainter").text();
    if (togglePainter != null && togglePainter != "") {
      this.eventService.sendEvent(
        EventSource.TOGGLE_TOOLBOX_CHANGED,
        togglePainter == "true"
      );
    }

    const goHome = $controller.find("data>participant-go-home").text();
    if (goHome == "true") {
      participants.forEach((p) => {
        const data: ParticipantGoHomeEvent = {
          participantId: p,
          active: goHome == "true",
        };

        this.eventService.sendEvent(EventSource.PARTICIPANT_GO_HOMEED, data);
      });
    }
  }

  toggleMute(type: TrackType, active: boolean) {
    this.log.debug("toggleMute", type, active);
    this.localTracks.forEach((r) => {
      if (r.getType() == type) {
        if (active) {
          r.unmute();
        } else {
          r.mute();
        }
      }
    });
  }

  sendControllerVoice(participants: string[], active: boolean) {
    if (!participants || participants.length < 1) {
      return;
    }
    this.smartBoard.sendControl(this.myParticipantId(), participants, {
      voice: active + "",
    });
  }

  sendControllerWhiteBoard(participants: string[], active: boolean) {
    if (!participants || participants.length < 1) {
      return;
    }
    this.smartBoard.sendControl(this.myParticipantId(), participants, {
      whiteboard: active + "",
    });
  }

  sendControllerGiftFabulous(participants: string[], active: number) {
    if (!participants || participants.length < 1) {
      return;
    }
    this.smartBoard.sendControl(this.myParticipantId(), participants, {
      gift: active,
    });
  }

  sendControllerGiftCoin(participants: string[], active: number) {
    if (!participants || participants.length < 1) {
      return;
    }
    this.smartBoard.sendControl(this.myParticipantId(), participants, {
      gift: active,
    });
  }

  sendControllerTogglePainter(toggle: boolean) {
    this.smartBoard.sendControl(this.myParticipantId(), [0], {
      togglePainter: toggle + "",
    });
  }

  sendControllerStudentMove(data: ParticipantMoveEvent) {
    this.smartBoard.sendControl(this.myParticipantId(), [data.participantId], {
      "participant-move": data.position,
    });
  }

  sendControllerStudentSelect(data: ParticipantSelectEvent) {
    this.smartBoard.sendControl(this.myParticipantId(), data.participantIds, {
      "participant-select": data.select + "",
    });
  }

  sendControllerStudentGoHome(participants: string[], active: boolean) {
    this.smartBoard.sendControl(this.myParticipantId(), participants, {
      "participant-go-home": active + "",
    });
  }

  myParticipantId() {
    return this.room && this.room.myUserId();
  }

  onConnectionSuccess() {
    this.log.debug("onConnectionSuccess", this, this.connection);
    this.eventService.sendXMPPConnectionStatus(CONNECTION_STATUS.ESTABLISHED);
  }

  onConnectionFailed() {
    this.log.debug("Connection Failed!");
  }

  onDeviceListChanged(devices: any) {
    console.info("current devices", devices);
  }

  onLocalTracks(tracks: any[]) {
    this.log.debug(TAG, "onLocalTracks",  arguments);

    this.localTracks = tracks;

    const localTracks = this.localTracks;

    const c: TrackWrapper = {
      uin: Utils.GetUinFromParticipant(this.myParticipantId()),
      participant: this.myParticipantId(),
      index:0, errors:0,
      changed:false,
      trackMap:{}
    };

    for (let i = 0; i < localTracks.length; i++) {
      const track = localTracks[i];

      this.log.debug("track", track.getType(), track);

      track.addEventListener(
        JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
        (audioLevel) => this.log.debug(`Audio Level local: ${audioLevel}`)
      );

      track.addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED, () =>
        this.log.debug("local track muted")
      );

      track.addEventListener(JitsiMeetJS.events.track.LOCAL_TRACK_STOPPED, () =>
        this.log.debug("local track stoped")
      );

      track.addEventListener(
        JitsiMeetJS.events.track.TRACK_AUDIO_OUTPUT_CHANGED,
        (deviceId) =>
          this.log.debug(`track audio output device was changed to ${deviceId}`)
      );

      if (this.isJoined) {
        this.room.addTrack(track);
      }

    // this.onUserJoined(this.myParticipantId(), null);
      // this.onTrackAdded( track);
      c.trackMap[track.getType()] = track;
      c.changed = true;
    }
    setTimeout(()=>{
      this.eventService.sendEvent(EventSource.TRACK_CHANGED, c);
    }, 2000)
  }

  onTrackRemoved(track: any){
    const participant = track.getParticipantId();

    this.log.debug(TAG,
      `onTrackRemoved: ${participant} type: ${track.getType()} "isLocal:" ${track.isLocal()}`
    );

  }

  onTrackAdded(track: any) {
    const participant = track.getParticipantId();

    this.log.debug(TAG, `onTrackAdded:  ${track}` );

    if (!track.isLocal()) {
      this.log.debug(TAG, 'addEventListener for', track);

      track.addEventListener(
        JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
        (audioLevel) => this.log.debug(`Audio Level remote: ${audioLevel}`)
      );

      track.addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED, () =>
        this.log.debug("remote track muted")
      );

      track.addEventListener(JitsiMeetJS.events.track.LOCAL_TRACK_STOPPED, () =>
        this.log.debug("remote track stoped")
      );

      track.addEventListener(
        JitsiMeetJS.events.track.TRACK_AUDIO_OUTPUT_CHANGED,
        (deviceId) =>
          this.log.debug(`track audio output device was changed to ${deviceId}`)
          );

      // const c: TrackWrapper = this.loadRemoteTrack(participant);
      // c.trackMap[track.getType()] = track;
      // c.changed = true;
      // this.eventService.sendEvent(EventSource.TRACK_CHANGED, c);
    }

    // this.log.debug(TAG, 'onTrackAdded', this.participants, track);
  }

  loadRemoteTrack(participant: string) {
    let t: TrackWrapper = this.getRemoteTrack(participant);
    if (!t) {
      const uin = Utils.GetUinFromParticipant(participant);
      t = {
        uin,
        participant,
        index: 0,
        trackMap: {}, errors: 0,
        changed: false, isPlayed: 0,
        createdTime: Utils.nowTime()
      };
      this.remoteTracks.push(t);
    }
    return t;
  }

  getRemoteTrack(participant: string) {
    const t: TrackWrapper = this.remoteTracks.find(
      (rt) => rt.participant === participant
    );
    return t;
  }

  getRemoteTracks() {
    return this.remoteTracks;
  }

  getRemoteTracksByUin(uin: string){
    this.log.debug(TAG, 'getRemoteTracksByUin', uin);

    const tks = this.getRemoteTracks();
    this.log.debug(TAG, 'getRemoteTracksByUin tks', tks);

    return tks.filter(e => 0 <= e.participant.indexOf(uin+'-'))
  }

  clearRemotedTracks(){
      this.remoteTracks = this.remoteTracks.filter(e => {
          if(e.needRemove){
              this.log.debug('remove track:', e.participant)
            }
            return !e.needRemove;
      });
  }

  removeParticipant(pId: string){
    this.log.debug(TAG, 'removeParticipant', pId, this.remoteTracks);
    const rId = this.getRemoteTracks().findIndex(e => e.participant === pId)
    this.remoteTracks.splice(rId, 1);
    this.log.debug(TAG, 'removeParticipant=>', this.remoteTracks);
  }

  onConferenceJoined() {
    this.log.debug(TAG, "conference joined!");

    this.eventService.sendXMPPConnectionStatus(
      CONNECTION_STATUS.CONFERENCE_JOINED
    );

    this.isJoined = true;

    for (let i = 0; i < this.localTracks.length; i++) {
      this.room.addTrack(this.localTracks[i]);
    }

    this.initSmartBoard();
  }

  onUserLeft(id: any, user: any) {
    this.log.debug(TAG, "onUserLeft", id, user);

    // const track: TrackWrapper = this.getRemoteTrack(id);
    // if (!track) {
    //   return;
    // }
    // const uin = Utils.GetUinFromParticipant(id);
    // if(0 === this.myParticipantId().indexOf(uin)){
    //   this.log.debug(TAG, "onUserLeft isMy", id);
    //   return;
    // }

    const rId = this.participants.findIndex(e  => e.getId() === id);
    this.participants.splice(rId, 1);
    this.log.debug(TAG, 'participants:', this.participants);

    // this.eventService.sendEvent(EventSource.USER_LEFT, user);

  }

  onUserJoined(id: string, user: any) {
    this.log.debug(TAG, "onUserJoined", id, 'user:', user ,
    'status:', user.getConnectionStatus());

    this.participants.push(user);
    this.log.debug(TAG, 'participants:', this.participants);

    // const tw: TrackWrapper = this.loadRemoteTrack(id);
    // this.log.debug(TAG, "user TrackWrapper", c);
    // this.eventService.sendEvent(EventSource.USER_JOINED, user);
  }

  users(){
    // this.log.debug(TAG, 'users', this.participants);

    const ps = this.participants.map(e => e.getId());
    // this.log.debug(TAG, 'users participantIds', ps);

    if(this.myParticipantId())
      ps.push(this.myParticipantId());

    const uins = ps.map(e => Utils.GetUinFromParticipant(e) )
    // this.log.debug(TAG, 'users uins', uins);

    return (new Set(uins));
  }

  getUser(participant: string){
    return this.participants.find(e => e.getId() === participant);
  }

  getUsersByUin(uin: string){
    // this.log.debug(TAG, 'getUsersByUin', uin, this.participants)
    const ps = this.participants.filter(e => 0 <= e.getId().indexOf(uin+'-'))
    // this.log.debug(TAG, 'getUsersByUin', uin, ps)
    return ps;
  }

  getUserTracks(user: any){
    // _connectionStatus
    return user._tracks
  }

  getConnectionStatus(user: any){
    return user ? user._connectionStatus: 'inactive';

  }
  getTrackType(track: any){
    return track && track.type;
  }

  dispose() {
    console.warn(TAG, "dispose!");
    this.localTracks.forEach(t => {
      t.dispose()
    })

    if(this.room)
      this.room.leave();

      // this.connection.disconnect();
  }

  onDisconnect() {

    console.warn(TAG, 'onDisconnect!');

    this.connection.removeEventListener(
      JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED,
      this.onConnectionSuccess
    );
    this.connection.removeEventListener(
      JitsiMeetJS.events.connection.CONNECTION_FAILED,
      this.onConnectionFailed
    );
    this.connection.removeEventListener(
      JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED,
      this.onDisconnect
    );
  }
}
